﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using System.Text;

namespace CYTS.Aoyou.Framework.Service
{
    public class WCFServiceHostPackager
    {
        public static void PackageWCFServiceHost(ServiceHost host, Type contractType, List<ServiceEndpoint> endpointConfigs)
        {
            PackageWCFServiceBehaviors(host);

            foreach (var endpoint in endpointConfigs)
            {
                if (endpoint.Category == ServiceCategory.WCFClassic)
                {
                    PackageWCFClassicServiceEndpoint(host, contractType, endpoint.Address);
                }
                else if (endpoint.Category == ServiceCategory.TCP)
                {
                    PackageTCPServiceEndpoint(host, contractType, endpoint.Address);
                }
                else if (endpoint.Category == ServiceCategory.RESTful)
                {
                    PackageRESTfulServiceEndpoint(host, contractType, endpoint.Address);
                }
                else if (endpoint.Category == ServiceCategory.MSMQ)
                {
                    PackageMSMQServiceEndpoint(host, contractType, endpoint.Address);
                }
                else if (endpoint.Category == ServiceCategory.WSHttp)
                {
                    PackageWSHttpServiceEndpoint(host, contractType, endpoint.Address);
                }
            }
        }

        private static void PackageWCFServiceBehaviors(ServiceHost host)
        {
            if (host.Description.Behaviors[typeof(ServiceDebugBehavior)] != null)
            {
                ServiceDebugBehavior sdb = (ServiceDebugBehavior)host.Description.Behaviors[typeof(ServiceDebugBehavior)];
                sdb.IncludeExceptionDetailInFaults = true;
            }

            host.Description.Behaviors.Add(new WCFServiceBehavior());

            //加大可序列化对象数量
            Type hostType = host.GetType();
            object obj = hostType.Assembly.CreateInstance("System.ServiceModel.Dispatcher.DataContractSerializerServiceBehavior",
            true, BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { false, Int32.MaxValue }, null, null);
            IServiceBehavior myServiceBehavior = obj as IServiceBehavior;
            if (myServiceBehavior != null)
            {
                host.Description.Behaviors.Add(myServiceBehavior);
            }
        }

        private static void PackageWCFClassicServiceEndpoint(ServiceHost host, Type contractType, string address)
        {
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            host.Description.Behaviors.Add(smb);

            BasicHttpBinding binding = new BasicHttpBinding();
            binding.MaxReceivedMessageSize = int.MaxValue;
            binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;

            CustomBinding cb = new CustomBinding(binding);
            BindingElementCollection bec = cb.Elements;

            //禁用Keep_Alive
            foreach (BindingElement be in bec)
            {
                Type thisType = be.GetType();
                if (be is HttpTransportBindingElement)
                {
                    HttpTransportBindingElement httpElement = (HttpTransportBindingElement)be;
                    httpElement.KeepAliveEnabled = false;
                }
            }

            host.AddServiceEndpoint(contractType, cb, address);
        }

        private static void PackageWSHttpServiceEndpoint(ServiceHost host, Type contractType, string address)
        {
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            host.Description.Behaviors.Add(smb);

            WSHttpBinding binding = new WSHttpBinding();
            binding.MaxReceivedMessageSize = int.MaxValue;
            binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;

            CustomBinding cb = new CustomBinding(binding);
            BindingElementCollection bec = cb.Elements;

            //禁用Keep_Alive
            foreach (BindingElement be in bec)
            {
                Type thisType = be.GetType();
                if (be is HttpTransportBindingElement)
                {
                    HttpTransportBindingElement httpElement = (HttpTransportBindingElement)be;
                    httpElement.KeepAliveEnabled = false;
                }
            }

            host.AddServiceEndpoint(contractType, cb, address);
        }

        private static void PackageTCPServiceEndpoint(ServiceHost host, Type contractType, string address)
        {
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = false;
            host.Description.Behaviors.Add(smb);

            NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
            binding.MaxReceivedMessageSize = int.MaxValue;
            binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
            host.AddServiceEndpoint(contractType, binding, address);

            ServiceMetadataBehavior metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
            if (metadataBehavior == null)
            {
                metadataBehavior = new ServiceMetadataBehavior();
                host.Description.Behaviors.Add(metadataBehavior);
            }
            Binding mexTcpBinding = MetadataExchangeBindings.CreateMexTcpBinding();
            host.AddServiceEndpoint(typeof(IMetadataExchange), mexTcpBinding, "mex");
        }

        private static void PackageRESTfulServiceEndpoint(ServiceHost host, Type contractType, string address)
        {
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            host.Description.Behaviors.Add(smb);

            WebHttpBinding webbinding = new WebHttpBinding(WebHttpSecurityMode.None);

            CustomBinding cb = new CustomBinding(webbinding);
            BindingElementCollection bec = cb.Elements;

            //禁用Keep_Alive
            foreach (BindingElement be in bec)
            {
                Type thisType = be.GetType();
                if (be is HttpTransportBindingElement)
                {
                    HttpTransportBindingElement httpElement = (HttpTransportBindingElement)be;
                    httpElement.KeepAliveEnabled = false;
                }
            }


            System.ServiceModel.Description.ServiceEndpoint endpoint = host.AddServiceEndpoint(contractType, cb, string.Empty);

            WebHttpBehavior behavior = new WebHttpBehavior();
            //behavior.DefaultBodyStyle = WebMessageBodyStyle.Wrapped;
            behavior.DefaultOutgoingResponseFormat = WebMessageFormat.Json;

            endpoint.Behaviors.Add(behavior);
        }

        private static void PackageMSMQServiceEndpoint(ServiceHost host, Type contractType, string address)
        {
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            host.Description.Behaviors.Add(smb);

            string typeName = contractType.Name.Substring(1);
            string queueName = string.Format(@".\private$\{0}", typeName.ToLower());
            address = string.IsNullOrEmpty(address) ? "net.msmq://localhost/private/" + typeName : address;
            if (!MessageQueue.Exists(queueName))
            {
                MessageQueue.Create(queueName, true);
                MessageQueue queue = new MessageQueue(queueName);
                if (queue != null)
                {
                    queue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
                }
            }

            NetMsmqBinding binding = new NetMsmqBinding(NetMsmqSecurityMode.None);
            binding.MaxReceivedMessageSize = int.MaxValue;
            binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
            host.AddServiceEndpoint(contractType, binding, address);

            ServiceMetadataBehavior metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
            if (metadataBehavior == null)
            {
                metadataBehavior = new ServiceMetadataBehavior();
                host.Description.Behaviors.Add(metadataBehavior);
            }
            Binding mexHttpBinding = MetadataExchangeBindings.CreateMexHttpBinding();
            host.AddServiceEndpoint(typeof(IMetadataExchange), mexHttpBinding, "mex");
        }
    }
}
